<template><div><p>本文档最新版为 <a href="https://learnku.com/docs/laravel/10.x" target="_blank" rel="noopener noreferrer">10.x</a>，旧版本可能放弃维护，推荐阅读最新版！</p>
<h2 id="laravel-的收费系统-cashier" tabindex="-1"><a class="header-anchor" href="#laravel-的收费系统-cashier"><span>Laravel 的收费系统 Cashier</span></a></h2>
<ul>
<li><a href="#introduction">简介</a></li>
<li><a href="#configuration">配置</a>
<ul>
<li><a href="#stripe-configuration">Stripe</a></li>
<li><a href="#braintree-configuration">Braintree</a></li>
<li><a href="#currency-configuration">货币配置</a></li>
</ul>
</li>
<li><a href="#subscriptions">订阅</a>
<ul>
<li><a href="#creating-subscriptions">创建订阅</a></li>
<li><a href="#checking-subscription-status">检查订阅状态</a></li>
<li><a href="#changing-plans">修改订阅计划</a></li>
<li><a href="#subscription-quantity">订阅量</a></li>
<li><a href="#subscription-taxes">订阅税额</a></li>
<li><a href="#cancelling-subscriptions">取消订阅</a></li>
<li><a href="#resuming-subscriptions">恢复订阅</a></li>
<li><a href="#updating-credit-cards">更新信用卡</a></li>
</ul>
</li>
<li><a href="#subscription-trials">试用订阅</a>
<ul>
<li><a href="#with-credit-card-up-front">有信用卡的情况下</a></li>
<li><a href="#without-credit-card-up-front">在没有信用卡的情况下</a></li>
</ul>
</li>
<li><a href="#handling-stripe-webhooks">处理 Stripe Webhooks</a>
<ul>
<li><a href="#defining-webhook-event-handlers">定义 Webhook 事件处理程序</a></li>
<li><a href="#handling-failed-subscriptions">订阅失败</a></li>
</ul>
</li>
<li><a href="#handling-braintree-webhooks">处理 Braintree Webhooks</a>
<ul>
<li><a href="#defining-braintree-webhook-event-handlers">定义 Webhook 事件处理程序</a></li>
<li><a href="#handling-braintree-failed-subscriptions">订阅失败</a></li>
</ul>
</li>
<li><a href="#single-charges">一次性收费</a></li>
<li><a href="#invoices">发票</a>
<ul>
<li><a href="#generating-invoice-pdfs">生成发票的 PDFs</a></li>
</ul>
</li>
</ul>
<h2 id="简介" tabindex="-1"><a class="header-anchor" href="#简介"><span>简介</span></a></h2>
<p>Laravel Cashier 提供了直观、流畅的接口来接入 <a href="https://stripe.com/" target="_blank" rel="noopener noreferrer">Stripe's</a> and <a href="https://www.braintreepayments.com/" target="_blank" rel="noopener noreferrer">Braintree's</a> 订阅付费服务。它可以处理几乎所有你写起来非常头疼付费订阅代码。除了提供基本的订阅管理之外，Cashier 还可以帮你处理优惠券，交换订阅、订阅“数量”、取消宽限期，甚至还可以生成pdf文档。</p>
<blockquote>
<p>{note} 如果你只是“一次性”的收费并且不提供订阅。你就不应该使用 Cashier 。可以直接使用 Stripe 和 Braintree 的 sdk。</p>
</blockquote>
<h2 id="配置" tabindex="-1"><a class="header-anchor" href="#配置"><span>配置</span></a></h2>
<h3 id="stripe" tabindex="-1"><a class="header-anchor" href="#stripe"><span>Stripe</span></a></h3>
<h4 id="composer" tabindex="-1"><a class="header-anchor" href="#composer"><span>Composer</span></a></h4>
<p>首先, 将 Stripe 的 Cashier 包添加到您的依赖项中：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line">composer <span class="token keyword">require</span> <span class="token string double-quoted-string">"laravel/cashier"</span><span class="token punctuation">:</span><span class="token string double-quoted-string">"~7.0"</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h4 id="服务提供者" tabindex="-1"><a class="header-anchor" href="#服务提供者"><span>服务提供者</span></a></h4>
<p>下一步, 在 <code v-pre>config/app.php</code> 配置文件中，注册 <code v-pre>Laravel\Cashier\CashierServiceProvider</code> <a href="https://learnku.com/docs/laravel/5.5/providers" target="_blank" rel="noopener noreferrer">服务提供者</a>.</p>
<h4 id="数据库迁移" tabindex="-1"><a class="header-anchor" href="#数据库迁移"><span>数据库迁移</span></a></h4>
<p>在使用 Cashier 之前，我们需要 <a href="https://learnku.com/docs/laravel/5.5/migrations" target="_blank" rel="noopener noreferrer">准备数据库</a>. 我们需要向您的 <code v-pre>users</code> 表中添加几个列，并创建一个新的 <code v-pre>subscriptions</code> 表来保存所有客户的订阅：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Schema</span><span class="token operator">::</span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'users'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$table</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'stripe_id'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'card_brand'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'card_last_four'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamp</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'trial_ends_at'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token class-name static-context">Schema</span><span class="token operator">::</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'subscriptions'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$table</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">increments</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">integer</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'user_id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'stripe_id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'stripe_plan'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">integer</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'quantity'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamp</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'trial_ends_at'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamp</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ends_at'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamps</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>一旦迁移文件建立好后，运行 Artisan 的 <code v-pre>migrate</code> 命令。</p>
<h4 id="billable-模型" tabindex="-1"><a class="header-anchor" href="#billable-模型"><span>Billable 模型</span></a></h4>
<p>下一步, 需要添加 <code v-pre>Billable</code> trait 到你的模型定义。trait 提供各种方法让您执行常见的帐单任务，例如：创建订阅、应用优惠券和更新信用卡信息：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">use</span> <span class="token package">Laravel<span class="token punctuation">\</span>Cashier<span class="token punctuation">\</span>Billable</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">User</span> <span class="token keyword">extends</span> <span class="token class-name">Authenticatable</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">use</span> <span class="token package">Billable</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h4 id="api-keys" tabindex="-1"><a class="header-anchor" href="#api-keys"><span>API Keys</span></a></h4>
<p>最后, 你需要在 <code v-pre>services.php</code> 配置文件中设置你的 Stripe key 。 你可以在 Stripe 的控制面板获取到相关的 API keys。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token string single-quoted-string">'stripe'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'model'</span>  <span class="token operator">=></span> <span class="token class-name class-name-fully-qualified static-context">App<span class="token punctuation">\</span>User</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'key'</span> <span class="token operator">=></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'STRIPE_KEY'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'secret'</span> <span class="token operator">=></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'STRIPE_SECRET'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="braintree" tabindex="-1"><a class="header-anchor" href="#braintree"><span>Braintree</span></a></h3>
<h4 id="braintree-注意事项" tabindex="-1"><a class="header-anchor" href="#braintree-注意事项"><span>Braintree 注意事项</span></a></h4>
<p>对于许多操作，Cashier 的 Stripe 和 Braintree 实现都是一样的。这两项服务都提供信用卡支付服务，但 Braintree 支持通过 PayPal 支付。然而，Braintree 也缺少一些由 Stripe 支持的功能。当你决定使用 Stripe 或 Braintree 时，你应该记住以下几点：</p>
<ul>
<li>Braintree 支持 PayPal ，而 Stripe 不支持.</li>
<li>Braintree 不支持订阅的 <code v-pre>increment</code> 和 <code v-pre>decrement</code> 方法。这是 Braintree 的限制，而不是 Cashier 限制。</li>
<li>Braintree 不支持基于百分比的折扣。这是 Braintree 的限制，而不是 Cashier 限制。</li>
</ul>
<h4 id="composer-1" tabindex="-1"><a class="header-anchor" href="#composer-1"><span>Composer</span></a></h4>
<p>首先，将 Braintree 的 Cashier 包添加到您的依赖项：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line">composer <span class="token keyword">require</span> <span class="token string double-quoted-string">"laravel/cashier-braintree"</span><span class="token punctuation">:</span><span class="token string double-quoted-string">"~2.0"</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h4 id="服务提供者-1" tabindex="-1"><a class="header-anchor" href="#服务提供者-1"><span>服务提供者</span></a></h4>
<p>下一步, 在 <code v-pre>config/app.php</code> 配置文件中，注册 <code v-pre>Laravel\Cashier\CashierServiceProvider</code> <a href="https://learnku.com/docs/laravel/5.5/providers" target="_blank" rel="noopener noreferrer">服务提供者</a>：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name class-name-fully-qualified static-context">Laravel<span class="token punctuation">\</span>Cashier<span class="token punctuation">\</span>CashierServiceProvider</span><span class="token operator">::</span><span class="token keyword">class</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h4 id="信用卡优惠计划" tabindex="-1"><a class="header-anchor" href="#信用卡优惠计划"><span>信用卡优惠计划</span></a></h4>
<p>在使用 Cashier 之前，你需要首先在 Braintree 控制面板定义一个 <code v-pre>plan-credit</code> 折扣。这个折扣会根据用户选择的支付选项匹配合适的折扣比例，比如选择年付还是月付。</p>
<p>在 Braintree 控制面板中配置的折扣总额可以随意填，Cashier 会在每次使用优惠券的时候根据我们自己的定制覆盖该默认值。由于 Braintree 不支持在订阅频率上来匹配折扣比例，所以这一优惠券是必需的。</p>
<h4 id="数据库迁移-1" tabindex="-1"><a class="header-anchor" href="#数据库迁移-1"><span>数据库迁移</span></a></h4>
<p>开始使用 Cashier 之前, 我们需要 <a href="https://learnku.com/docs/laravel/5.5/migrations" target="_blank" rel="noopener noreferrer">准备一下数据库</a>.我们需要在数据库的 <code v-pre>users</code> 表中新增几列，以及创建一个新的 <code v-pre>subscriptions</code> 表来存储客户的订阅信息：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Schema</span><span class="token operator">::</span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'users'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$table</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'braintree_id'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'paypal_email'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'card_brand'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'card_last_four'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamp</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'trial_ends_at'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token class-name static-context">Schema</span><span class="token operator">::</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'subscriptions'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$table</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">increments</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">integer</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'user_id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'braintree_id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token keyword type-declaration">string</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'braintree_plan'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">integer</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'quantity'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamp</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'trial_ends_at'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamp</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ends_at'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">nullable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$table</span><span class="token operator">-></span><span class="token function">timestamps</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>一旦迁移文件建立好后，运行 Artisan 的 <code v-pre>migrate</code> 命令。</p>
<h4 id="billable-模型-1" tabindex="-1"><a class="header-anchor" href="#billable-模型-1"><span>Billable 模型</span></a></h4>
<p>下一步, 添加 <code v-pre>Billable</code> trait 到你的模型定义 ：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">use</span> <span class="token package">Laravel<span class="token punctuation">\</span>Cashier<span class="token punctuation">\</span>Billable</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">User</span> <span class="token keyword">extends</span> <span class="token class-name">Authenticatable</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">use</span> <span class="token package">Billable</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h4 id="api-keys-1" tabindex="-1"><a class="header-anchor" href="#api-keys-1"><span>API Keys</span></a></h4>
<p>下一步, 你应该在 <code v-pre>services.php</code> 文件中，参考下面的配置选项:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token string single-quoted-string">'braintree'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'model'</span>  <span class="token operator">=></span> <span class="token class-name class-name-fully-qualified static-context">App<span class="token punctuation">\</span>User</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'environment'</span> <span class="token operator">=></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'BRAINTREE_ENV'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'merchant_id'</span> <span class="token operator">=></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'BRAINTREE_MERCHANT_ID'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'public_key'</span> <span class="token operator">=></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'BRAINTREE_PUBLIC_KEY'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'private_key'</span> <span class="token operator">=></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'BRAINTREE_PRIVATE_KEY'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然后，你应该向 <code v-pre>AppServiceProvider</code> 服务提供商的 <code v-pre>boot</code> 方法中，添加以下的 Braintree SDK 调用：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Braintree_Configuration</span><span class="token operator">::</span><span class="token function">environment</span><span class="token punctuation">(</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'services.braintree.environment'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Braintree_Configuration</span><span class="token operator">::</span><span class="token function">merchantId</span><span class="token punctuation">(</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'services.braintree.merchant_id'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Braintree_Configuration</span><span class="token operator">::</span><span class="token function">publicKey</span><span class="token punctuation">(</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'services.braintree.public_key'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Braintree_Configuration</span><span class="token operator">::</span><span class="token function">privateKey</span><span class="token punctuation">(</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'services.braintree.private_key'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="货币配置" tabindex="-1"><a class="header-anchor" href="#货币配置"><span>货币配置</span></a></h3>
<p>Cashier 使用美元（USD）作为默认货币。你可以通过在服务提供者的 <code v-pre>boot</code> 方法中调用 <code v-pre>Cashier::useCurrency</code> 方法来更改默认的货币。这个 <code v-pre>useCurrency</code> 方法接受两个字符串参数：货币和货币符号：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">use</span> <span class="token package">Laravel<span class="token punctuation">\</span>Cashier<span class="token punctuation">\</span>Cashier</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token class-name static-context">Cashier</span><span class="token operator">::</span><span class="token function">useCurrency</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'eur'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'€'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="订阅" tabindex="-1"><a class="header-anchor" href="#订阅"><span>订阅</span></a></h2>
<h3 id="创建订阅" tabindex="-1"><a class="header-anchor" href="#创建订阅"><span>创建订阅</span></a></h3>
<p>创建订阅，首先需要获取到一个 billable 模型实例，这通常是 <code v-pre>App\User</code> 的一个实例。一旦您获取了模型实例，您可以使用 <code v-pre>newSubscription</code> 方法创建模型的订阅:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span> <span class="token operator">=</span> <span class="token class-name static-context">User</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">newSubscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'premium'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">create</span><span class="token punctuation">(</span><span class="token variable">$stripeToken</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code v-pre>newSubscription</code> 方法的第一个参数应该是订阅的名称。如果您的应用程序只提供一个订阅，那么您可以将其设置为 <code v-pre>main</code> or <code v-pre>primary</code>。<br>
第二个参数是用户订阅的 Stripe / Braintree 计划。这个值应该与 Stripe 或 Braintree 中的标识符对应。</p>
<p><code v-pre>create</code> 方法接受一个 Stripe 信用卡 / 源令牌，它将开始订阅，并使用客户 ID 和其他相关的账单信息更新数据库。</p>
<h4 id="用户其他的详细信息" tabindex="-1"><a class="header-anchor" href="#用户其他的详细信息"><span>用户其他的详细信息</span></a></h4>
<p>如果您想要指定用户其他的详细信息，您可以通过将它们作为第二个参数传递给 <code v-pre>create</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">newSubscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'monthly'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">create</span><span class="token punctuation">(</span><span class="token variable">$stripeToken</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'email'</span> <span class="token operator">=></span> <span class="token variable">$email</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>要了解更多关于 Stripe 或 Braintree 支持的额外字段，请查看 Stripe 的内容 <a href="https://stripe.com/docs/api#create_customer" target="_blank" rel="noopener noreferrer">创建客户文档</a> 或对应的 <a href="https://developers.braintreepayments.com/reference/request/customer/create/php" target="_blank" rel="noopener noreferrer">Braintree 文档</a>。</p>
<h4 id="优惠券" tabindex="-1"><a class="header-anchor" href="#优惠券"><span>优惠券</span></a></h4>
<p>如果您想在创建订阅时使用优惠券，您可以使用 <code v-pre>withCoupon</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">newSubscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'monthly'</span><span class="token punctuation">)</span></span>
<span class="line">     <span class="token operator">-></span><span class="token function">withCoupon</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'code'</span><span class="token punctuation">)</span></span>
<span class="line">     <span class="token operator">-></span><span class="token function">create</span><span class="token punctuation">(</span><span class="token variable">$stripeToken</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="检查订阅状态" tabindex="-1"><a class="header-anchor" href="#检查订阅状态"><span>检查订阅状态</span></a></h3>
<p>一旦用户在您的应用程序订阅了，您可以使用各种方便的方法轻松地检查他们的订阅状态。首先，如果用户有一个激活的订阅，那么 <code v-pre>subscribed</code> 的方法返回 <code v-pre>true</code> ，即使订阅当前处于试用阶段：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscribed</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这个 <code v-pre>subscribed</code> 方法还可以在 <a href="https://learnku.com/docs/laravel/5.5/middleware" target="_blank" rel="noopener noreferrer">路由中间件</a> 使用，允许您根据用户的订阅状态对路由和控制器进行访问。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handle</span><span class="token punctuation">(</span><span class="token variable">$request</span><span class="token punctuation">,</span> <span class="token class-name type-declaration">Closure</span> <span class="token variable">$next</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$request</span><span class="token operator">-></span><span class="token function">user</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span> <span class="token variable">$request</span><span class="token operator">-></span><span class="token function">user</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">subscribed</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 用户并没有完成支付...</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token function">redirect</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'billing'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$next</span><span class="token punctuation">(</span><span class="token variable">$request</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如果您想要确定用户是否仍然处于测试阶段，您可以使用 <code v-pre>onTrial</code> 方法。这个方法对于向用户显示他们仍然处于试用期的警告是很有用的。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">onTrial</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>可以使用 <code v-pre>subscribedToPlan</code> 方法可以基于给定的 Stripe / Braintree 计划 ID 来确定用户是否订阅了的该计划，在本例中，我们将确定用户的 <code v-pre>main</code> 订阅是否激活了 <code v-pre>monthly</code> 计划：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscribedToPlan</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'monthly'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h4 id="取消订阅状态" tabindex="-1"><a class="header-anchor" href="#取消订阅状态"><span>取消订阅状态</span></a></h4>
<p>为了确定用户是否曾经订阅，但是已经取消了他们的订阅，您可以使用 <code v-pre>cancelled</code> 方法:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">cancelled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>您还可以确定用户是否已经取消了订阅，但是仍然处于订阅的「宽限期」，直到订阅完全过期为止。例如，如果用户在3月5日取消了原定于3月10日到期的订阅，那么用户将在3月10日之前进行「宽限期」。请注意，在此期间，<code v-pre>subscribed</code> 方法仍然返回 <code v-pre>true</code> ：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">onGracePeriod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="修改订阅计划" tabindex="-1"><a class="header-anchor" href="#修改订阅计划"><span>修改订阅计划</span></a></h3>
<p>用户在您的应用程序中订阅了之后，他们可能会偶尔想要更改一个新的订阅计划。要将一个用户切换到一个新的订阅，需将订阅计划的标识符传递给 <code v-pre>swap</code> 方法：<br>
$user = App\User::find(1);</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">swap</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'provider-plan-id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>如果用户在试用期，试用期的期限会被保留。另外，如果订阅的数量存在「份额」，那么该份额也将保持。<br>
如果你想在更改用户订阅计划的时候取消用户当前订阅的试用期，可以使用 <code v-pre>skipTrial</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span></span>
<span class="line">        <span class="token operator">-></span><span class="token function">skipTrial</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">        <span class="token operator">-></span><span class="token function">swap</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'provider-plan-id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="订阅量" tabindex="-1"><a class="header-anchor" href="#订阅量"><span>订阅量</span></a></h3>
<blockquote>
<p>{note}<br>
订阅数量仅由 Cashier 的 Stripe 支持。Braintree 没有一个对应于 Stripe 的「数量」的特性。</p>
</blockquote>
<p>有些时候订阅是会受「数量」影响的。举个例子，你的应用程序的付费方式可能是每个账户 $10 /人/月。你可以使用 <code v-pre>incrementQuantity</code> 和 <code v-pre>decrementQuantity</code> 方法轻松的增加或者减少你的订阅数量：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span> <span class="token operator">=</span> <span class="token class-name static-context">User</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">incrementQuantity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 对当前的订阅数量加 5 ...</span></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">incrementQuantity</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">decrementQuantity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 对订阅的当前数量减去 5 ...</span></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">decrementQuantity</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>或者，您可以使用 <code v-pre>updateQuantity</code> 方法设置一个特定的数量：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">updateQuantity</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>要获得更多关于订阅量的信息，请参考 <a href="https://stripe.com/docs/subscriptions/quantities" target="_blank" rel="noopener noreferrer">Stripe 文档</a>.</p>
<h3 id="订阅税额" tabindex="-1"><a class="header-anchor" href="#订阅税额"><span>订阅税额</span></a></h3>
<p>要指定用户在订阅中支付的税率百分比，在计费模式上实现 <code v-pre>taxPercentage</code> 方法，并在0到100之间返回一个数值，不超过2个小数点。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">taxPercentage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token number">20</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code v-pre>taxPercentage</code> 方法使你能够在模型的基础上应用税率，这对于一个跨越多个国家和税率的用户群来说可能是有帮助的。</p>
<blockquote>
<p>{note} <code v-pre>taxPercentage</code> 方法只适用于订阅付费模式。如果你用 charges 来做 完成一次性收费的，你需要手工指定税率。</p>
</blockquote>
<h3 id="取消订阅" tabindex="-1"><a class="header-anchor" href="#取消订阅"><span>取消订阅</span></a></h3>
<p>要取消订阅，只需在用户的订阅上调用 <code v-pre>cancel</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>当一个订阅被取消时，Cashier 会自动在你的数据库中设置 <code v-pre>ends_at</code> 列。这个列用于知道 <code v-pre>subscribed</code> 的方法何时应该开始返回 <code v-pre>false</code> 。例如，如果客户在3月1日取消订阅，但是订阅计划直到3月5日才会结束，<code v-pre>subscribed</code> 方法将会返回 <code v-pre>true</code> 一直到3月5日。</p>
<p>您可以使用 <code v-pre>onGracePeriod</code> 方法确定用户是否已经取消了订阅，但是仍然在一个「宽限期」：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">onGracePeriod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如果你想马上取消订阅，请在用户的订阅中调用 <code v-pre>cancelNow</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">cancelNow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h3 id="恢复订阅" tabindex="-1"><a class="header-anchor" href="#恢复订阅"><span>恢复订阅</span></a></h3>
<p>如果用户取消了他们的订阅，可以使用 <code v-pre>resume</code> 的方法来恢复他们的订阅。用户还 <strong>必须</strong> 在他们的宽限期内才能恢复订阅：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">resume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>如果用户取消订阅，然后在订阅完全过期之前恢复订阅，他们将不会立即被计费。相反，他们的订阅将会被重新激活，需要按照原本的支付流程再次进行支付。</p>
<h3 id="更新信用卡信息" tabindex="-1"><a class="header-anchor" href="#更新信用卡信息"><span>更新信用卡信息</span></a></h3>
<p><code v-pre>updateCard</code> 方法可以用来更新客户的信用卡信息。该方法接受一个 Stripe 的 token ，并将新信用卡分配为默认的支付源：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">updateCard</span><span class="token punctuation">(</span><span class="token variable">$stripeToken</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h2 id="试用订阅" tabindex="-1"><a class="header-anchor" href="#试用订阅"><span>试用订阅</span></a></h2>
<h3 id="有信用卡的情况下" tabindex="-1"><a class="header-anchor" href="#有信用卡的情况下"><span>有信用卡的情况下</span></a></h3>
<p>如果你想要给你的客户提供试用期，同时还要收集支付方法信息，那么你应该在创建订阅时使用 <code v-pre>trialDays</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span> <span class="token operator">=</span> <span class="token class-name static-context">User</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">newSubscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'monthly'</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">trialDays</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">create</span><span class="token punctuation">(</span><span class="token variable">$stripeToken</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>此方法将在数据库内的订阅记录中设置试用期结束日期，并指示 Stripe / Braintree 在此日期之前不开始向客户收费。</p>
<blockquote>
<p>{note} 如果客户没有在试用期结束之前取消订阅，订阅会被自动结算，所以需要在试用期结束之前通知你的客户。</p>
</blockquote>
<p>您可以使用用户实例的 <code v-pre>onTrial</code> 方法或订阅实例的 <code v-pre>onTrial</code> 方法来确定用户是否处于试用期。下面两个例子是相同的：<br>
if ($user-&gt;onTrial('main')) {<br>
//<br>
}</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">subscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">onTrial</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="没有信用卡的情况下" tabindex="-1"><a class="header-anchor" href="#没有信用卡的情况下"><span>没有信用卡的情况下</span></a></h3>
<p>如果您希望在不收集用户的支付方法信息的情况下提供试用期，您可以简单地将用户记录的 <code v-pre>trial_ends_at</code> 列设置为您想要的试用结束日期。这通常是在用户注册过程中完成的：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span> <span class="token operator">=</span> <span class="token class-name static-context">User</span><span class="token operator">::</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token comment">// 填充其他用户属性...</span></span>
<span class="line">    <span class="token string single-quoted-string">'trial_ends_at'</span> <span class="token operator">=></span> <span class="token class-name static-context">Carbon</span><span class="token operator">::</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">addDays</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>{note}请确保在你的模型中已经为 <code v-pre>trial_ends_at</code> <a href="https://learnku.com/docs/laravel/5.5/eloquent-mutators#date-mutators" target="_blank" rel="noopener noreferrer">日期转换器</a>。</p>
</blockquote>
<p>Cashier 把这种类型的试用引用为「generic trial」, 因为它并没有关联任何已存在的订阅。如果当前的日期没有超过 <code v-pre>trial_ends_at</code> 的值，那么 <code v-pre>User</code> 实例上的 <code v-pre>onTrial</code> 方法将返回 <code v-pre>true</code> ：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">onTrial</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// 用户在试用期内...</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如果您希望明确地知道用户处于「generic」的试用期，并且还没有创建实际的订阅，那么您可以使用 <code v-pre>onGenericTrial</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">onGenericTrial</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// 用户在 「generic」 试用期内...</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>一旦您准备好为用户创建实际的订阅，您可以像往常一样使用 <code v-pre>newSubscription</code> 方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span> <span class="token operator">=</span> <span class="token class-name static-context">User</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">newSubscription</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'main'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'monthly'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">create</span><span class="token punctuation">(</span><span class="token variable">$stripeToken</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="处理-stripe-webhooks" tabindex="-1"><a class="header-anchor" href="#处理-stripe-webhooks"><span>处理 Stripe Webhooks</span></a></h2>
<p>Stripe 和 Braintree 都可以通过 webhooks 通知你的应用程序。要处理 Stripe Webhooks ，定义一个路由指向 Cashier 的 webhook 控制器的。该控制器将处理所有传入的 webhook 请求，并将它们发送到适当的控制器方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Route</span><span class="token operator">::</span><span class="token function">post</span><span class="token punctuation">(</span></span>
<span class="line">    <span class="token string single-quoted-string">'stripe/webhook'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'</span></span>
<span class="line"><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>{note} 一旦你注册了你的路由之后，一定要在你的 Stripe 控制面板设置中配置webhook URL。</p>
</blockquote>
<p>默认情况下，该控制器将自动处理在多次失败的支付尝试后自动取消该订阅(根据您的 Stripe 设置所定义);但是，我们很快就会发现，您可以扩展这个控制器来处理任何您喜欢的 webhook 事件。</p>
<h4 id="webhooks-csrf-保护" tabindex="-1"><a class="header-anchor" href="#webhooks-csrf-保护"><span>Webhooks &amp; CSRF 保护</span></a></h4>
<p>因为 Stripe webhooks 需要绕过 Laravel 的 <a href="https://learnku.com/docs/laravel/5.5/csrf" target="_blank" rel="noopener noreferrer">CSRF 保护</a>，一定要在 <code v-pre>VerifyCsrfToken</code> 中间件中列出 URI ，或者列出 <code v-pre>web</code> 中间件组之外的路由：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">protected</span> <span class="token variable">$except</span> <span class="token operator">=</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'stripe/*'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="定义-webhook-事件处理程序" tabindex="-1"><a class="header-anchor" href="#定义-webhook-事件处理程序"><span>定义 Webhook 事件处理程序</span></a></h3>
<p>Cashier 对于失败的支付自动进行取消订阅的处理，但是如果你有其他的 Stripe webhook 事件，你想要处理，可以简单地扩展 Webhook 控制器。您的方法名应该与 Cashier 期望的约定相符，具体来说，方法应该是用 <code v-pre>handle</code> 前缀，和您希望处理的 Stripe webhook 的「驼峰」名称。例如，如果您希望处理 <code v-pre>invoice.payment_succeeded</code> webhook ，你应该添加一个 <code v-pre>handleInvoicePaymentSucceeded</code> 方法到控制器：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">Laravel<span class="token punctuation">\</span>Cashier<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers<span class="token punctuation">\</span>WebhookController</span> <span class="token keyword">as</span> CashierController<span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">WebhookController</span> <span class="token keyword">extends</span> <span class="token class-name">CashierController</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * Handle a Stripe webhook.</span>
<span class="line">     *</span>
<span class="line">     * <span class="token keyword">@param</span>  <span class="token class-name"><span class="token keyword">array</span></span>  <span class="token parameter">$payload</span></span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name">Response</span></span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handleInvoicePaymentSucceeded</span><span class="token punctuation">(</span><span class="token variable">$payload</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 处理事件</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="订阅失败" tabindex="-1"><a class="header-anchor" href="#订阅失败"><span>订阅失败</span></a></h3>
<p>如果客户的信用卡过期了怎么办?不用担心，Cashier 会有一个 Webhook 控制器，它可以很容易地取消客户的订阅。如上所述，您所需要做的就是指定一个路由到该控制器：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Route</span><span class="token operator">::</span><span class="token function">post</span><span class="token punctuation">(</span></span>
<span class="line">    <span class="token string single-quoted-string">'stripe/webhook'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'</span></span>
<span class="line"><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>就是这样！失败的支付将被控制器捕获和处理。当 Stripe 判断订阅失败时，控制器将取消客户的订阅(通常是在三次失败的付款尝试之后)。</p>
<h2 id="处理-braintree-webhooks" tabindex="-1"><a class="header-anchor" href="#处理-braintree-webhooks"><span>处理 Braintree Webhooks</span></a></h2>
<p>Stripe 和 Braintree 都可以通过 webhooks 通知你的应用程序。要处理 Braintree webhook ，可以定义一个路由指向 Cashier 的 webhook 控制器。该控制器将处理所有传入的 webhook 请求，并将它们发送到适当的控制器方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Route</span><span class="token operator">::</span><span class="token function">post</span><span class="token punctuation">(</span></span>
<span class="line">    <span class="token string single-quoted-string">'braintree/webhook'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'</span></span>
<span class="line"><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>{note} 一旦你注册了你的路由之后，一定要在你的 Braintree 控制面板设置中配置webhook URL。</p>
</blockquote>
<p>默认情况下，该控制器将自动处理在多次失败的支付尝试后自动取消该订阅(根据您的 Braintree 设置所定义);但是，我们很快就会发现，您可以扩展这个控制器来处理任何您喜欢的 webhook 事件。</p>
<h4 id="webhooks-csrf-保护-1" tabindex="-1"><a class="header-anchor" href="#webhooks-csrf-保护-1"><span>Webhooks &amp; CSRF 保护</span></a></h4>
<p>因为 Braintree webhooks 需要绕过 Laravel 的 <a href="https://learnku.com/docs/laravel/5.5/csrf" target="_blank" rel="noopener noreferrer">CSRF 保护</a>，所以一定要在 <code v-pre>VerifyCsrfToken</code> 中间件中列出这个 URI ，或者列出 <code v-pre>web</code> 中间件组之外的路由:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">protected</span> <span class="token variable">$except</span> <span class="token operator">=</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'braintree/*'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="定义-webhook-事件处理程序-1" tabindex="-1"><a class="header-anchor" href="#定义-webhook-事件处理程序-1"><span>定义 Webhook 事件处理程序</span></a></h3>
<p>Cashier 对于失败的支付自动进行取消订阅的处理，但是如果你想对 Braintree webhook 事件进行额外的处理，可以简单的扩展一下 Webhook 控制器。你的方法名称应该与 Cashier 的预设惯例一直，特别的是，方法名应该以 <code v-pre>handle</code> 为前缀，然后以 「驼峰」命名的方式加上你要处理的 Braintree webhook 的名称。比如，如果你希望去处理 <code v-pre>dispute_opened</code> webhook, 你应该添加一个 <code v-pre>handleDisputeOpened</code> 方法到控制器：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">Braintree<span class="token punctuation">\</span>WebhookNotification</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">Laravel<span class="token punctuation">\</span>Cashier<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers<span class="token punctuation">\</span>WebhookController</span> <span class="token keyword">as</span> CashierController<span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">WebhookController</span> <span class="token keyword">extends</span> <span class="token class-name">CashierController</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * Handle a Braintree webhook.</span>
<span class="line">     *</span>
<span class="line">     * <span class="token keyword">@param</span>  <span class="token class-name">WebhookNotification</span>  <span class="token parameter">$webhook</span></span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name">Response</span></span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handleDisputeOpened</span><span class="token punctuation">(</span><span class="token class-name type-declaration">WebhookNotification</span> <span class="token variable">$notification</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// Handle The Event</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="订阅失败-1" tabindex="-1"><a class="header-anchor" href="#订阅失败-1"><span>订阅失败</span></a></h3>
<p>如果客户的信用卡过期了怎么办?不用担心，Cashier 会有一个 Webhook 控制器，它可以很容易地取消客户的订阅。如上所述，您所需要做的就是指定一个路由到该控制器：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Route</span><span class="token operator">::</span><span class="token function">post</span><span class="token punctuation">(</span></span>
<span class="line">    <span class="token string single-quoted-string">'braintree/webhook'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'</span></span>
<span class="line"><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>就是这样！失败的支付将被控制器捕获和处理。当 Braintree 判断订阅失败时，控制器将取消客户的订阅(通常是在三次失败的付款尝试之后)。</p>
<h2 id="一次性收费" tabindex="-1"><a class="header-anchor" href="#一次性收费"><span>一次性收费</span></a></h2>
<h3 id="单次付费" tabindex="-1"><a class="header-anchor" href="#单次付费"><span>单次付费</span></a></h3>
<blockquote>
<p>{note} 当使用 Stripe 时，<code v-pre>charge</code> 方法会接受你想要在 <strong>应用程序使用的货币的最小面值</strong> 中收取的金额。然而，当使用 Braintree 时，您应该将全部的美元金额传递给 <code v-pre>charge</code> 方法：</p>
</blockquote>
<p>如果您想要对订阅的客户的信用卡进行「一次」的收费，那么您可以在一个 billable 模型实例上使用 <code v-pre>charge</code> 方法。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// Stripe Accepts Charges In Cents...</span></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">charge</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// Braintree Accepts Charges In Dollars...</span></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">charge</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code v-pre>charge</code> 方法接受一个数组作为它的第二个参数，允许您将任何您希望的选项传递给底层的 Stripe / Braintree 费用创建。在创建收费时，请参考 Stripe 或 Braintree 文档，了解您可以选择的选项：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">charge</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'custom_option'</span> <span class="token operator">=></span> <span class="token variable">$value</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>当支付结算失败是 <code v-pre>charge</code> 方法会抛出一个异常。如果结算成功，会返回完成的 Stripe / Braintree 响应：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">try</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$response</span> <span class="token operator">=</span> <span class="token variable">$user</span><span class="token operator">-></span><span class="token function">charge</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> <span class="token variable">$e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">//</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="费用与发票" tabindex="-1"><a class="header-anchor" href="#费用与发票"><span>费用与发票</span></a></h3>
<p>有时候你只是需要完成一个一次性的支付，但也要为收费生成一张发票信息，这样你可以提供 PDF 的票据给你的客户。<code v-pre>invoiceFor</code> 方法就是做这个的。例如，让我们给客户开具一个 $5.00 的「一次性费用」发票：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// Stripe 以美分结算...</span></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">invoiceFor</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'One Time Fee'</span><span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// Braintree 以美元结算...</span></span>
<span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">invoiceFor</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'One Time Fee'</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>生成发票将立即对用户的信用卡收取费用。invoiceFor 方法可以接受一个数据作为第三个参数，允许你在 Stripe / Braintree 结算时传递任何可用的参数：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$user</span><span class="token operator">-></span><span class="token function">invoiceFor</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'One Time Fee'</span><span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'custom-option'</span> <span class="token operator">=></span> <span class="token variable">$value</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>{note} <code v-pre>invoiceFor</code> 方法在当多次尝试失败支付后也会产生一个 Stripe 发票。如果你不需要为失败重试的支付生成发票，你需要在第一次失败结算是就调用 Stripe 的 API 关闭它们。</p>
</blockquote>
<h2 id="发票" tabindex="-1"><a class="header-anchor" href="#发票"><span>发票</span></a></h2>
<p>你可以使用 <code v-pre>invoices</code> 方法轻松获取 billable 模型的所有发票信息：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$invoices</span> <span class="token operator">=</span> <span class="token variable">$user</span><span class="token operator">-></span><span class="token function">invoices</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// Include pending invoices in the results...</span></span>
<span class="line"><span class="token variable">$invoices</span> <span class="token operator">=</span> <span class="token variable">$user</span><span class="token operator">-></span><span class="token function">invoicesIncludingPending</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>当将发票信息列出来给用户时，你可能需要使用发票的辅助函数来显示相关的发票信息。例如，你可能希望将发票列表以表格的形式显示，允许用户轻松的下载它们：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token operator">&lt;</span>table<span class="token operator">></span></span>
<span class="line">    @<span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$invoices</span> <span class="token keyword">as</span> <span class="token variable">$invoice</span><span class="token punctuation">)</span></span>
<span class="line">        <span class="token operator">&lt;</span>tr<span class="token operator">></span></span>
<span class="line">            <span class="token operator">&lt;</span>td<span class="token operator">></span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">$invoice</span><span class="token operator">-></span><span class="token function">date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">toFormattedDateString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">></span></span>
<span class="line">            <span class="token operator">&lt;</span>td<span class="token operator">></span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">$invoice</span><span class="token operator">-></span><span class="token function">total</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">></span></span>
<span class="line">            <span class="token operator">&lt;</span>td<span class="token operator">></span><span class="token operator">&lt;</span>a href<span class="token operator">=</span><span class="token string double-quoted-string">"/user/invoice/{{ <span class="token interpolation"><span class="token variable">$invoice</span><span class="token operator">-></span><span class="token property">id</span></span> }}"</span><span class="token operator">></span>Download<span class="token operator">&lt;</span><span class="token operator">/</span>a<span class="token operator">></span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">></span></span>
<span class="line">        <span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">></span></span>
<span class="line">    @<span class="token keyword">endforeach</span></span>
<span class="line"><span class="token operator">&lt;</span><span class="token operator">/</span>table<span class="token operator">></span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="生成发票的-pdfs" tabindex="-1"><a class="header-anchor" href="#生成发票的-pdfs"><span>生成发票的 PDFs</span></a></h3>
<p>从路由或控制器中，使用 <code v-pre>downloadInvoice</code> 方法生成发票的 PDF 下载。该方法将自动生成适当的 HTTP 响应，将下载发送到浏览器:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Request</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token class-name static-context">Route</span><span class="token operator">::</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'user/invoice/{invoice}'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token class-name type-declaration">Request</span> <span class="token variable">$request</span><span class="token punctuation">,</span> <span class="token variable">$invoiceId</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$request</span><span class="token operator">-></span><span class="token function">user</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">downloadInvoice</span><span class="token punctuation">(</span><span class="token variable">$invoiceId</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'vendor'</span>  <span class="token operator">=></span> <span class="token string single-quoted-string">'Your Company'</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token string single-quoted-string">'product'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'Your Product'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="译者署名" tabindex="-1"><a class="header-anchor" href="#译者署名"><span>译者署名</span></a></h2>
<table>
<thead>
<tr>
<th>用户名</th>
<th>头像</th>
<th>职能</th>
<th>签名</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://learnku.com/users/15752" target="_blank" rel="noopener noreferrer">@孤雪飘寒</a></td>
<td><img src="https://cdn.learnku.com/uploads/avatars/15752_1493141445.jpeg" alt="15752_1493141445.jpeg"></td>
<td>翻译</td>
<td>全桟工程师，<a href="https://github.com/piaohan" target="_blank" rel="noopener noreferrer">Github</a>，<a href="http://blog.csdn.net/msmile_my" target="_blank" rel="noopener noreferrer">CSDN</a></td>
</tr>
</tbody>
</table>
<hr>
<blockquote>
<p>{note} 欢迎任何形式的转载，但请务必注明出处，尊重他人劳动共创开源社区。</p>
<p>转载请注明：本文档由 Laravel China 社区 <a href="https://laravel-china.org/" target="_blank" rel="noopener noreferrer">laravel-china.org</a> 组织翻译，详见 <a href="https://learnku.com/laravel/t/5756/laravel-55-document-translation-call-come-and-join-the-translation" target="_blank" rel="noopener noreferrer">翻译召集帖</a>。</p>
<p>文档永久地址： <a href="https://learnku.com/docs/laravel" target="_blank" rel="noopener noreferrer">《Laravel 中文文档》</a></p>
</blockquote>
</div></template>


